const cloud = require('wx-server-sdk')
const lib = require('./common/lib')

cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})

const db = cloud.database()
const _  = db.command

async function createCollection(id){
  return await db.createCollection(id)
}


function purviewCaptcher(user, captcher){
  if (user.WEB_UID === 'administrator') {
    return captcher
  } else {
    return captcher.where({UID: user.UID})
  }
  // if (uid === 'administrator' || !uid) {
  //   captcher = this.collection
  // } else {
  //   captcher = this.collection.where({UID})
  // }
}

const condition = ['where', 'doc', 'limit', 'orderBy', 'skip', 'field']
function conditionCaptcher(param, captcher){
  if (typeof param === 'object' && !Array.isArray(param)) {
    let keys = Object.keys(param)
    keys.forEach(ky=>{
      if (condition.includes(ky)) {
        if (ky === 'doc') {
          captcher = captcher[ky](param[ky])
        }else if (ky === 'where') {
          if (param[ky]._id || param[ky]._key) {
            let findKy = param[ky]._id ? '_id' : param[ky]._key ? '__datakey__' : ''
            if (findKy) captcher = captcher.where({[findKy]: (param[ky]._id || param[ky]._key)})
          } else {
            let tmp = {[ky]: {__value__: null}}
            if (param[ky].UID || (param[ky].__value__ && param[ky].__value__.UID)) {
              tmp[ky].UID = (param[ky].UID || param[ky].__value__.UID)
            }
            if (param[ky].__value__) {
              delete param[ky].__value__.UID
              tmp[ky].__value__ = param[ky].__value__
            } else {
              delete param[ky].UID
              tmp[ky].__value__ = param[ky]
            }
            captcher = captcher[ky](tmp[ky])
          }
        } else {
          const charType = ['string', 'number', 'boolean']
          if (charType.includes(typeof param[ky])) {
            captcher = captcher[ky](param[ky])  
          } else {
            if (typeof param[ky] === 'object') {
              const specialKey = ['_id', '_key', 'UID']
              let tmp = {[ky]: {__value__: {}}}
              if (param[ky] === null) {
                tmp[ky] = param[ky]
              } else {
                Object.keys(param[ky]).forEach(k => {
                  if (specialKey.indexOf(k) > -1) {
                    tmp[ky][k] = param[ky][k]
                  } else {
                    tmp[ky]['__value__'][k] = param[ky][k]
                    // tmp[ky][('__value__.'+k)] = param[ky][k]
                  }
                })
              }
              captcher = captcher[ky](tmp[ky])
            }
          }
        }
      }
    })
  }
  return captcher
}

class Storage{
  constructor(coll, ctx){
    
    // 集合实例
    this.collection = coll
    
    // getWXContext
    // https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-sdk-api/utils/Cloud.getWXContext.html
    this.user = {
      CLIENTIP: ctx.CLIENTIP,
      CLIENTIPV6: ctx.CLIENTIPV6,
      ENV: ctx.CLIENTIP,  // 'local'
      OPENID: ctx.OPENID,
      UNIONID: ctx.UNIONID,
      UID: (ctx.UNIONID||ctx.OPENID),
      WEB_UID: ctx.web_uid,  // PC端用户id，一般为administrator
      SOURCE: ctx.SOURCE
    },

    this.profile = {
      APPID: ctx.APPID,
    }
  }

  async setItem(k, v={}, type){
    let user = this.user
    user.UID = v.UID || user.UID
    let UID = user.UID || user.WEB_UID
    v.UID = UID
    v.user = user

    if (v.expire) {  // 一定要是时间戳
      let nowTime = (new Date()).getTime()
      let expireTime = nowTime + v.expire
      v.expire = expireTime
    }

    let tmp = {data: v}

    // 按照id来更新文档
    if (v.byId) {
      delete v.byId
      delete tmp.data.byId
      if (type === 'update') {
        return await this.collection.doc(k).update(tmp)
      } else {
        return await this.collection.doc(k).set(tmp)
      }
    }

    // 按照 key 来更新文档
    let res = await this.collection.where({__datakey__: k, UID}).get()
    if (res.data && res.data.length) {
      let ret = res.data[0]
      if (type === 'update') {
        return await this.collection.doc(ret._id).update(tmp)
      } else {
        return await this.collection.doc(ret._id).set(tmp)
      }
    } else {
      return await this.collection.add(tmp)
    }
  }

  async updateItem(k, v={}){
    return await this.setItem(k, v, 'update')
  }

  async getList(uid, param){
    let user = this.user
    let UID = uid || user.UID

    let captcher = null
    
    // if (uid === 'administrator' || !uid) {
    //   captcher = this.collection
    // } else {
    //   captcher = this.collection.where({UID})
    // }

    captcher = purviewCaptcher(user, this.collection)

    if (typeof params === 'object' && !Array.isArray(param)) {
      captcher = conditionCaptcher(param, captcher)
    }

    const res = await captcher.get()
    const ret = []
    if (res.data && res.data.length) {
      res.data.forEach(item=>{
        if (item.expire) {  // 时间戳
          let nowTime = (new Date()).getTime()
          if (item.expire > nowTime) {
            ret.push(item)
          }
        } else {
          ret.push(item)
        }
      })
    }
    return {data: ret}
  }

  async getItem(k, param={}){
    let user = this.user
    let UID = user.UID || user.WEB_UID
    if (!k) k = 'getByParam'

    let captcher = null
    if (param.byId) {
      captcher = this.collection.doc(param.id)
    } else if (k !== 'getByParam') {
      captcher = purviewCaptcher(user, this.collection).where({__datakey__: k})
      // if (UID === 'administrator') {
      //   captcher = this.collection.where({__datakey__: k})
      // } else {
      //   captcher = this.collection.where({__datakey__: k, UID})
      // }
    } else {
      // if (UID === 'administrator') {
      //   captcher = this.collection
      // } else {
      //   captcher = this.collection.where({UID})
      // }
      captcher = conditionCaptcher(param, purviewCaptcher(user, this.collection))
    }

    let ret = null
    let res = await captcher.limit(0).get()
    if (res.data) {
      ret = (res.data[0]||res.data)
      if (ret.expire) {  // 时间戳
        let nowTime = (new Date()).getTime()
        if (ret.expire <= nowTime) {
          res.data = []
          ret = null
          await this.removeItem(k)
        }
      }
    }
    // return await this.collection.where({__datakey__: k, UID}).get()
    return {data: ret}
  }

  getUser(){
    return {data: [this.user]}
  }

  async removeItem(k, param={}){
    if (param.byId) {
      return await this.collection.where({
        _id: _.in(param.id)
      }).remove()
    } else {
      let res = await this.getItem(k)
      let ret = (res.data && res.data[0]) || res.data || {}
      if (ret._id) return await this.collection.doc(ret._id).remove()
    }

    // let res = await this.getItem(k)
    // if (res.data && res.data.length) {
    //   let ret = res.data[0]
    //   try {
    //     return await this.collection.doc(ret._id).remove()
    //   } catch (e) {
    //     console.error(e)
    //   }
    // }
  }

  async clear(){
    let user = this.user
    let UID = user.UID
    if (UID) return await this.collection.where({UID}).remove()
  }
}

const correctActions = [ 'clear', 'setItem', 'getItem', 'updateItem', 'removeItem', 'getUser', 'getList' ]
// 云函数入口函数
exports.main = async (event, context) => {
  let collectionName = event.__collection__
  let action = event.__action__
  let dataKey = event.__datakey__
  let web_uid = event.__web_uid__
  let wxContext = cloud.getWXContext()
  let currentColl = null
  let param = Object.assign({}, event)
  wxContext.web_uid = web_uid  // pc端云操作传递的用户id，因为pc端不能以openid区分用户，所以需要独立设置一个用户名id

  if (action === 'create') {
    try {
      await createCollection(collectionName)
      currentColl = db.collection(collectionName)
    } catch (error) {
      // currentColl = db.collection(collectionName)
    }
    return {}
  }
  
  if (correctActions.indexOf(action)>-1) {
    currentColl = db.collection(collectionName)

    // 如果传入了用户的UID，切换sotrage的上下文
    if ((param.uid||param.UID) && !param._id) {
      let UID = param.uid||param.UID
      let res = await currentColl.where({UID}).limit(1).get()
      if (res.data && res.data.length) {
        wxContext = res.data[0].user
      } 
    }

    // 如果传入了文档id，切换sotrage的上下文
    if (param._id) {
      let res = await currentColl.doc(param._id).get()
      if (res.data && res.data.length) {
        wxContext = res.data[0].user
      }
    }
    
    const instance = new Storage(currentColl, wxContext)
    if (instance[action]) {
      return instance[action](dataKey, param)
    }
  }

  return event
}
